package com.hm.health.records.ecgpatch;

import static com.hm.health.MyApplication.getHBDTestClient;

import android.util.Log;

import com.goodix.comsdk.entity.MeasureResult;
import com.goodix.comsdk.sdk.OnHBDDataListener;
import com.goodix.http.jsonparams.request.UploadCfgInfoParams;
import com.goodix.libromablecom.sdk.cardiff.CardiffConstants;
import com.goodix.libromablecom.sdk.cardiff.CardiffDeviceController;
import com.goodix.libromablecom.sdk.cardiff.arguments.StartArgumentsKeyEnum;
import com.goodix.utils.TimeUtil;
import com.google.gson.Gson;
import com.hm.health.MyApplication;
import com.hm.health.entity.BleDevicesInfo;
import com.hm.health.records.ITestRecord;

import java.util.List;

public class EcgPatchRecord implements ITestRecord {

    private final static String TAG = "EcgPatchRecord";
    public static final int OFFLINE_MODE_INT = 0;
    public static final int ONLINE_MODE_INT = 1;
    private static final int VALID_START_YEAR = 2021;
    // 文件头
    public static final String RAWDATA_HEADER = "TimeStamp,Mode,PacketId";

    public static final String OPTIMIZE_RAWDATA_HEADER = "FRAME_ID,ACCX,ACCY,ACCZ,CH0,ALGO_RESULT0,ALGO_RESULT1,REF_RESULT0,FLAG2";
    // 历史记录文件头
    public static final String RESULT_HEADER = "TimeStamp,Mode,PacketId,ALGO_RESULT0,ALGO_RESULT1,FLAG4,FLAG5";

    // 时间戳信息
    public enum DATA_TIME_MSG {
        DATA_MODE,
        YEAR,
        MONTH,
        DATE,
        HOUR,
        MINUTE,
        SECOND,
        MSECOND,
        PACKET_ID,
        TEST_ID
    };

    private List<BleDevicesInfo> mBleDeviceInfos;

    private EcgPatchWoker mWorker;

    private String mTestInfo;

    private String mStartTime;

    private EcgPatchSector mSector;

    private long mCurrentTime = 0;

    private int mSampleRate = 500;

    private int mFrameTimeGap = 0;

    private String mTestDeviceMac = ""; // 测试主设备mac地址

    private int mCompareDeviceHeartRate = 0; // 对比设备的心率值

    private boolean mEncryption = false;

    private int mFrameNum = 0;

    private int mHeaderNum = 0;

    private boolean mOptimizeRawdataSize = true;

    public interface SyncSectorCallback {
        public void onProgress(int totalNum, int remainNum, int finishedSectorId);
        public void onSaveFinish();
    }

    public List<Integer> startQueryLostSectors(int maxSectorId, int minSectorId, SyncSectorCallback callback) {
        return mWorker.startQueryLostSectors(callback, maxSectorId, minSectorId);
    }

    public void stopQueryLostSectors() {
        mWorker.stopQueryLostSectors();
    }

    private OnHBDDataListener mOnHBDDataListener = new OnHBDDataListener() {
        @Override
        public void onDataChanged(String dataType, int[] dataArr) {

            if (dataType.equals(StartArgumentsKeyEnum.AppFunction_ECG)) {
                mFrameNum++;

                Log.i(TAG, "[sector info] frame, num:" + mFrameNum + ", type:" + dataType);

                if (mSector == null) {
                    Log.e(TAG, "receive a frame data, but sector is null");
                    return;
                }
                EcgPatchSector.EcgPatchFrame frame = new EcgPatchSector.EcgPatchFrame();
                if (mCurrentTime < mSector.getSectorInfo().getBeginTimeStamp()) {
                    Log.e(TAG, "it's never happened, current time:" + mCurrentTime + " is less than sector begin time:" + mSector.getSectorInfo().getBeginTimeStamp());
                    mCurrentTime = mSector.getSectorInfo().getBeginTimeStamp();
                }

                frame.mTimestamp = mCurrentTime;
                mCurrentTime += mFrameTimeGap;

                //EcgPatchSectorInfo info = mSector.getSectorInfo();
                //int mode = info.getMode();
                //frame.mMode = mode;
                frame.mMode = mSector.getSectorInfo().getMode();
                frame.mSectorId = mSector.getSectorInfo().getSectorId();
                frame.mFrameId = dataArr[0];
                dataArr[CardiffConstants.RawdataColumn.REF_RESULT0.ordinal()] = mCompareDeviceHeartRate;
                if (mOptimizeRawdataSize) {
                    int[] optimizeData = new int[9];
                    optimizeData[0] = dataArr[CardiffConstants.RawdataColumn.FRAME_ID.ordinal()];
                    optimizeData[1] = dataArr[CardiffConstants.RawdataColumn.ACCX.ordinal()];
                    optimizeData[2] = dataArr[CardiffConstants.RawdataColumn.ACCY.ordinal()];
                    optimizeData[3] = dataArr[CardiffConstants.RawdataColumn.ACCZ.ordinal()];
                    optimizeData[4] = dataArr[CardiffConstants.RawdataColumn.CH0.ordinal()];
                    optimizeData[5] = dataArr[CardiffConstants.RawdataColumn.ALGO_RESULT0.ordinal()];
                    optimizeData[6] = dataArr[CardiffConstants.RawdataColumn.ALGO_RESULT1.ordinal()];
                    optimizeData[7] = dataArr[CardiffConstants.RawdataColumn.REF_RESULT0.ordinal()];
                    optimizeData[8] = dataArr[CardiffConstants.RawdataColumn.FLAG2.ordinal()];
                    frame.setRawdata(optimizeData);
                } else {
                    frame.setRawdata(dataArr);
                }

                int[] result = new int[4];
                result[0] = dataArr[CardiffConstants.RawdataColumn.ALGO_RESULT0.ordinal()];
                result[1] = dataArr[CardiffConstants.RawdataColumn.ALGO_RESULT1.ordinal()];
                result[2] = dataArr[CardiffConstants.RawdataColumn.FLAG4.ordinal()]; // lead state
                result[3] = dataArr[CardiffConstants.RawdataColumn.FLAG5.ordinal()]; // last frame, sector frame num
                frame.setRecord(result);
                mSector.addFrame(frame);

                // check col to set sector total frame num
                // result[3] > 0: the last frame of a sector
                if (result[3] > 0) {
                    mSector.setFrameTotalNum(result[3]);
                    Log.i(TAG, "Set sector frame num to " + mSector.getFrameTotalNum() + ", sector id " + mSector.getSectorInfo().getSectorId());

                    // all frame received in one sector
                    if (mSector.checkSectorFramesAllReceived()) {
                        // add to worker and set null

                        if (mSector.checkSectorValid()) {
                            mWorker.addSectors(mSector);
                            Log.i(TAG, "success to generate a sector, prepare to store sector " + mSector.getSectorInfo().getSectorId());
                        }
                    }

                    mSector = null;
                }
            } else if (dataType.equals(StartArgumentsKeyEnum.ALG_ECG_DATA_TIME_MSG)) {

                mFrameNum = 0;
                mHeaderNum++;

                int sectorId = dataArr[DATA_TIME_MSG.PACKET_ID.ordinal()];
                int mode = dataArr[DATA_TIME_MSG.DATA_MODE.ordinal()];
                int testRandomId = dataArr[DATA_TIME_MSG.TEST_ID.ordinal()];

                int year = dataArr[DATA_TIME_MSG.YEAR.ordinal()];
                int month = dataArr[DATA_TIME_MSG.MONTH.ordinal()];
                int date = dataArr[DATA_TIME_MSG.DATE.ordinal()];
                int hour = dataArr[DATA_TIME_MSG.HOUR.ordinal()];
                int minute = dataArr[DATA_TIME_MSG.MINUTE.ordinal()];
                int second = dataArr[DATA_TIME_MSG.SECOND.ordinal()];
                int milsec = dataArr[DATA_TIME_MSG.MSECOND.ordinal()];
                String timeStr = String.format("%04d%02d%02d%02d%02d%02d", year, month,
                        date, hour, minute, second);
                long timestamp = TimeUtil.getEcgTime(timeStr) + milsec;
                mCurrentTime = timestamp;

                Log.i(TAG, "[EcgPatchData][sector info] header num:" + mHeaderNum + ", sector id:" + sectorId + ", timestamp:" + timestamp + ", timeStr:" + timeStr + ", testRandomId:" + testRandomId);

                if (mSector != null) {
                    Log.e(TAG, "old sector not saved, sector info:" + mSector.toString());
                }

                boolean isValid = true;
                if (year < VALID_START_YEAR) {
                    Log.e(TAG, "time of year is invalid, year=" + year + ", sectorId=" + sectorId);
                    isValid = false;
                }

                mSector = new EcgPatchSector(sectorId, timestamp, mTestInfo, mode, isValid);
                generateSectorHeaderAndDescription();

            } else if (dataType.equals(StartArgumentsKeyEnum.ALG_ECG_CLASSIFICATION_RESULT)) {

            }
        }

        @Override
        public void onMeasureResult(MeasureResult result) {
            Log.d(TAG, "ECG flash onMeasureResult:" + result.toString());

            if (result.getFunctionName().equals(StartArgumentsKeyEnum.AppFunction_HR)) {
                if (!result.getDeviceAddress().equals(mTestDeviceMac)) {
                    mCompareDeviceHeartRate = result.getIntValue();
                    Log.d(TAG, "ECG flash onMeasureResult: compare HR " + mCompareDeviceHeartRate);
                }
            }
        }
    };


    private void generateSectorHeaderAndDescription() {

        if (mSector == null) {
            Log.e(TAG, "no need to generate sector header, sector is null");
            return;
        }

        UploadCfgInfoParams uploadCfgInfoParams = MyApplication.getInstance()
                .getUploadCfgParam(mBleDeviceInfos, mSector.getSectorInfo().getBeginTimeStamp());
        String header = new Gson().toJson(uploadCfgInfoParams) + "\n";

        mSector.setSectorHeader(header);

        mSector.setSectorResultColumnDescription(RESULT_HEADER);

        String suffixDescription = CardiffDeviceController.getInstance().getFileHeader(StartArgumentsKeyEnum.AppFunction_ECG).trim();
        if (mOptimizeRawdataSize) {
            suffixDescription = OPTIMIZE_RAWDATA_HEADER;
        }
        String rawdataDescription = RAWDATA_HEADER + "," + suffixDescription;
        mSector.setSectorColumnDescription(rawdataDescription);
    }

    @Override
    public void startSaveRecord(String startTime, String testerInfo, List<BleDevicesInfo> devicesInfos) {

        Log.i(TAG, " start to save ecg patch record");
        mStartTime = startTime;
        mTestInfo = testerInfo;
        mBleDeviceInfos = devicesInfos;

        // mSampleRate =
        mFrameTimeGap = 1000 / mSampleRate;
        mWorker = new EcgPatchWoker(testerInfo, mSampleRate);

        getHBDTestClient().registerHBDDataListener(mOnHBDDataListener);
        mTestDeviceMac = MyApplication.getHBDTestClient().getRomaDevice().getDeviceAddress();

        mEncryption = MyApplication.getHbdOptionIni().isRawDataEncrypted();
        Log.i(TAG, " ======start to save ecg patch record");

    }

    @Override
    public void stopSaveRecord() {
        Log.i(TAG, " stop to save ecg patch record");
        getHBDTestClient().unRegisterHBDDataListener(mOnHBDDataListener);
        mWorker.quit();
    }

    @Override
    public void onMeasureResult(List<MeasureResult> results) {

    }
}
